/**
 * 
 */
package com.homesoft.runtime.metadata.modle;

import com.homesoft.runtime.log.ILog;
import com.homesoft.runtime.log.LogLevel;
import com.homesoft.runtime.log.LogManager;

/**
 * @author lihg
 * 
 */
public class MetadataPath {

	public final static char MetadataPathSeparatorChar = '/';

	public final static String MetadataPathSeparator = "/";

	private final static ILog _logger = LogManager.getLogger(MetadataPath.class);

	// / <summary>
	// / 合并两个元数据路径
	// / </summary>
	// / <param name="path1"></param>
	// / <param name="path2"></param>
	// / <returns></returns>
	public static String Combine(String path1, String path2) {
		if ((path1 == null) || (path2 == null)) {
			_logger.writeTraceLog(LogLevel.Error, "参数值为空");
			throw new argumentNullException((path1 == null) ? "path1" : "path2");
		}
		if (path2.length() == 0) {
			return path1;
		}
		if (path1.length() == 0) {
			return path2;
		}
		if (IsPathRooted(path2)) {
			return path2;
		}
		char ch = path1[path1.Length - 1];
		if (ch != MetadataPathSeparatorChar) {
			return path1 + MetadataPathSeparatorChar + path2;
		}
		return path1 + path2;
	}

	public static boolean IsPathRooted(String path) {
		if (path != null) {
			int length = path.Length;
			if (length >= 1 && path[0] == MetadataPathSeparatorChar) {
				return true;
			}
		}
		return false;
	}

	// / <summary>
	// / 获取指定路径上的值
	// / </summary>
	// / <param name="obj"></param>
	// / <param name="objectPath">不区分大小写</param>
	// / <returns></returns>
	public static object GetValue(Object obj, String objectPath)
{
    if (String.IsNullOrEmpty(objectPath))
    {
        return obj;
    }
    objectPath = objectPath.Trim(MetadataPathSeparatorChar);
    String[] entries = objectPath.Split(new char[] { MetadataPathSeparatorChar },
        StringSplitOptions.RemoveEmptyEntries);
    int len = entries.Length;
    int index = 0;
    object result = obj;
    while (index < len)
    {
        if (result == null)
        {
            _logger.WriteTraceLog(LogLevel.Error, "The objectPath could not be parsed！" + obj.GetType().Name + ":" + objectPath);
            throw new MissingMemberException("The objectPath could not be parsed！"
                                        + obj.GetType().Name + ":" + objectPath);
        }
        String segment = entries[index];
        int arrayCharStart = segment.IndexOf('[');
        if (arrayCharStart > 0)
        {
            String propertyName = segment.SubString(0, arrayCharStart);
            object arraylist = GetPropertyValue(obj, objectPath, result, propertyName);
            String indexStr = segment.SubString(arrayCharStart + 1);
            indexStr = indexStr.Trim('[', ']', '\"');
            int arrayIndex;
            if (arraylist is IExtendDataList)
            {
                result = ((IExtendDataList)arraylist).GetValue(indexStr);
            }
            else if (arraylist is IMetadataList)
            {
                IMetadataList metadataList = (IMetadataList)arraylist;
                if (metadataList.ContainsKey(indexStr))
                {
                    result = metadataList[indexStr];
                }
            }
            else if (arraylist is IList && int.TryParse(indexStr, out arrayIndex))
            {
                IList list = (IList)arraylist;
                result = list[arrayIndex];
            }
            else
            {
                _logger.WriteTraceLog(LogLevel.Error, "The objectPath could not be parsed！" + obj.GetType().Name + ":" + objectPath);
                throw new MissingMemberException("The objectPath could not be parsed！"
                    + obj.GetType().Name + ":" + objectPath);
            }
        }
        else
        {
            result = GetPropertyValue(obj, objectPath, result, segment);
        }
        index++;
    }

    return result;
}

	// / <summary>
	// / 设置值到指定的路径上
	// / </summary>
	// / <param name="obj"></param>
	// / <param name="objectPath"></param>
	// / <param name="value"></param>
	// / <returns></returns>
	public static boolean SetValue(object obj, String objectPath, object value)
{

    if (String.IsNullOrEmpty(objectPath))
    {
        return false;
    }
    objectPath = objectPath.Trim(MetadataPathSeparatorChar);
    int index = objectPath.LastIndexOf(MetadataPathSeparatorChar);
    int lastArray = objectPath.LastIndexOf('[');
    object preObj = null;
    if (index > 0 || lastArray > 0)
    {
        String preObjectPath = objectPath.SubString(0, index > lastArray ? index : lastArray);
        preObj = GetValue(obj, preObjectPath);
    }
    else
    {
        preObj = obj;
    }
    String propertyName = objectPath.SubString(index + 1);

    int arrayCharStart = propertyName.IndexOf('[');
    if (arrayCharStart > 0)
    {
        String indexStr = propertyName.SubString(arrayCharStart + 1);
        indexStr = indexStr.Trim('[', ']', '\"');
        int arrayIndex;
        if (preObj is IExtendDataList)
        {
            ((IExtendDataList)preObj).SetValue(indexStr, value as String);
        }
        else if (int.TryParse(indexStr, out arrayIndex))
        {
            IList list = (IList)preObj;
            if (arrayIndex >= list.Count)
            {
                list.Add(value);
            }
            else
            {
                list[arrayIndex] = value;
            }
        }
        else if (preObj is IMetadataList)
        {
            IMetadataList metadataList = (IMetadataList)preObj;
            try
            {
                IMetadataObject metadataObject = metadataList[indexStr];
                metadataList[metadataList.IndexOf(metadataObject)] = value;
            }
            catch (Exception)
            {
                metadataList.Add(value);
            }
        }
    }
    else
    {
        System.Reflection.PropertyInfo propertyInfo = GetProperty(preObj.GetType(), propertyName);
        if (propertyInfo == null || !propertyInfo.CanWrite)
        {
            return false;
        }
        propertyInfo.SetValue(preObj, value, null);
    }
    return true;
}

	// / <summary>
	// / 获取属性的值
	// / </summary>
	// / <param name="obj"></param>
	// / <param name="objectPath"></param>
	// / <param name="result"></param>
	// / <param name="propertyName"></param>
	// / <returns></returns>
	private static object GetPropertyValue(object obj, String objectPath, object result, String propertyName) {
		Type type = result.GetType();
		System.Reflection.PropertyInfo propertyInfo = GetProperty(type, propertyName);
		if (propertyInfo != null) {
			return propertyInfo.GetValue(result, null);
		} else {
			System.Reflection.FieldInfo fieldInfo = GetField(type, propertyName);
			if (fieldInfo != null) {
				return fieldInfo.GetValue(result);
			} else {
				_logger.WriteTraceLog(LogLevel.Error, "The objectPath could not be parsed！" + obj.GetType().Name + ":"
						+ objectPath);
				throw new MissingMemberException("The objectPath could not be parsed！" + obj.GetType().Name + ":"
						+ objectPath);
			}
		}
	}

	// / <summary>
	// / 获取给定名称的属性，
	// / 用于ObjectPath路径解析，ObjectPath不区分大小写，所以此属性不区分大小写.
	// / </summary>
	// / <param name="type"></param>
	// / <param name="propertyName"></param>
	// / <returns></returns>
	private static System.Reflection.PropertyInfo GetProperty(Type type, String propertyName)
{
    try
    {
        System.Reflection.PropertyInfo property = type.GetProperty(propertyName);
        if (property != null)
        {
            return property;
        }
    }
    catch
    {
    }
    return type.GetProperties().FirstOrDefault(item =>
        String.Equals(item.Name, propertyName, StringComparison.OrdinalIgnoreCase));
}

	// / <summary>
	// / 获取给定名称的字段，用于ObjectPath路径解析，
	// / ObjectPath不区分大小写，所以此字段不区分大小写.
	// / </summary>
	// / <param name="type"></param>
	// / <param name="fieldName"></param>
	// / <returns></returns>
	private static System.Reflection.FieldInfo GetField(Type type, String fieldName)
{
    try
    {
        System.Reflection.FieldInfo field = type.GetField(fieldName, BindingFlags.Instance
        | BindingFlags.NonPublic | BindingFlags.Public);
        if (field != null)
        {
            return field;
        }
    }
    catch (Exception)
    {
    }
    return type.GetFields(BindingFlags.Instance
        | BindingFlags.NonPublic | BindingFlags.Public).FirstOrDefault(fieldInfo =>
        String.Equals(fieldInfo.Name, fieldName, StringComparison.OrdinalIgnoreCase));
}
}
